package com.ccp.dev.workflow.listener;

import com.ccp.dev.core.basic.consts.SystemConst;
import com.ccp.dev.core.basic.util.ContextUtil;
import com.ccp.dev.core.basic.util.PropertyUtil;
import com.ccp.dev.core.basic.util.UUIDUtils;
import com.ccp.dev.core.util.AppUtil;
import com.ccp.dev.form.model.FormDef;
import com.ccp.dev.form.service.FormDefService;
import com.ccp.dev.system.model.SysProperty;
import com.ccp.dev.system.model.SysUser;
import com.ccp.dev.workflow.bpmconst.BpmConst;
import com.ccp.dev.workflow.model.ActDefModel;
import com.ccp.dev.workflow.model.BpmNodeSet;
import com.ccp.dev.workflow.model.ProcessRun;
import com.ccp.dev.workflow.service.*;
import com.ccp.dev.workflow.service.bpm.thread.MessageUtil;
import com.ccp.dev.workflow.service.bpm.thread.TaskThreadService;
import org.flowable.engine.delegate.DelegateExecution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;

/**
 * ClassDescribe:
 * 开始事件监听器
 *
 * @author :wangcheng
 * Date: 2019-05-13
 */
@Component
public class StartEventListener extends BaseNodeEventListener {
    private static final Logger LOGGER = LoggerFactory.getLogger(StartEventListener.class);

    @Resource
    private ActDefModelService actDefModelService;
    @Resource
    private ProcessRunService processRunService;
    @Resource
    private BpmNodeSetService bpmNodeSetService;
    @Resource
    private BpmFormRunService bpmFormRunService;
    @Resource
    private IdentityService identityService;

    @Override
    protected Integer getScriptType() {
        return BpmConst.START_SCRIPT;
    }

    @Override
    protected void execute(DelegateExecution execution, String procDefId, String nodeId) {
        LOGGER.debug("nodeId" + nodeId);
        handExtSubProcess(execution);
    }

    /**
     * 处理子流程。
     * <pre>
     * 1.如果流程变量当中有“innerPassVars”变量，那么这个是一个外部子流程的调用。
     * 2.创建processrun记录。
     * 3.准备流程实例id为初始化执行堆栈做准备。
     * 4.处理运行表单。
     * 5.通知任务执行人。
     * </pre>
     * @param execution
     */
    private void handExtSubProcess(DelegateExecution execution) {
        // 非子流程调用直接返回
        if (execution.getVariable(BpmConst.PROCESS_INNER_VARNAME) == null) {
            return;
        }
        Map<String,Object> variables = (Map<String, Object>) execution.getVariable(BpmConst.PROCESS_INNER_VARNAME);
        // 是否是外部调用
        Boolean isExtCall = (Boolean) variables.get(BpmConst.IS_EXTERNAL_CALL);
        String parentProcDefId = (String) variables.get(BpmConst.FLOW_PARENT_PROCDEFID);
        String instanceId = execution.getProcessInstanceId();

        // 准备流程实例id 为初始化执行堆栈做准备
        TaskThreadService.addExtSubProcess(instanceId);

        String procDefId = execution.getProcessDefinitionId();
        String parentRunId = (String) variables.get(BpmConst.FLOW_RUNID);
        ProcessRun parentProcessRun = processRunService.getById(parentRunId);
        // 添加processRun
        ProcessRun processRun = initProcessRun(procDefId, instanceId, variables, parentProcessRun);

        String runId = processRun.getRunId();

        // 设置流程运行变量
        execution.setVariable(BpmConst.FLOW_RUNID, processRun);
        execution.setVariables(variables);

        // 处理运行时表单
        if (isExtCall != null && !isExtCall) {
            bpmFormRunService.addFormRun(procDefId, runId, instanceId, parentProcDefId);
        }
    }

    /**
     * 插入流程运行记录
     * @param procDefId 流程定义id
     * @param instanceId 流程实例id
     * @param variables 流程变量
     * @param parentProcessRun 父流程执行对象
     * @return
     */
    private ProcessRun initProcessRun(String procDefId, String instanceId, Map<String, Object> variables, ProcessRun parentProcessRun) {
        String businessKey = (String) variables.get(BpmConst.FLOW_BUSINESSKEY);
        String parentProcDefId = (String) variables.get(BpmConst.FLOW_PARENT_PROCDEFID);

        ActDefModel actDefModel = actDefModelService.getByProcDefId(procDefId);
        ProcessRun processRun = new ProcessRun();

        // 获取子流程配置的表单
        BpmNodeSet bpmNodeSet = bpmNodeSetService.getStartBpmNodeSet(procDefId, parentProcDefId);
        if (bpmNodeSet == null) {
            String msg = "外部子流程【"+actDefModel.getName()+"】未设置表单!";
            MessageUtil.addMsg(msg);
            throw new RuntimeException(msg);
        }else {
            String formDefId = getExtSubProcessFormKey(bpmNodeSet.getFormKey(), parentProcessRun.getFormDefId(), actDefModel.getName());
            processRun.setFormDefId(formDefId);
        }

        SysUser sysUser = (SysUser) ContextUtil.getCurrentUser();
        if (sysUser != null) {
            processRun.setCreator(sysUser.getFullName());
            processRun.setCreatorId(sysUser.getUserId());
        } else {
            processRun.setCreator(SystemConst.SYSTEMUSERNAME);
            processRun.setCreatorId(SystemConst.SYSTEMUSERID);
        }
        if (ActDefModel.STATUS_TEST.equals(actDefModel.getStatus())) {
            processRun.setIsFormal(ProcessRun.TEST_RUNNING);
        }else {
            processRun.setIsFormal(ProcessRun.FORMAL_RUNNING);
        }

        processRun.setProcDefId(procDefId);
        processRun.setModelId(actDefModel.getId());
        processRun.setProcessName(actDefModel.getName());
        processRun.setProcInstId(instanceId);

        String subject = (String) variables.get(BpmConst.FLOW_RUN_SUBJECT);
        processRun.setSubject(subject);
        processRun.setBusinessKey(businessKey);
        processRun.setCreateTime(new Date());
        processRun.setStatus(ProcessRun.STATUS_RUNNING);
        processRun.setRunId(UUIDUtils.getUUIDFor32());
        processRun.setParentId(parentProcessRun.getRunId());
        processRun.setTypeId(actDefModel.getTypeId());
        processRun.setPkName(parentProcessRun.getPkName());
        processRun.setTableName(parentProcessRun.getTableName());
        processRun.setDsAlias(parentProcessRun.getDsAlias());
        // 判断是否需要增加全局流水号TODO
        boolean rtn = PropertyUtil.getBooleanByAlias(SysProperty.GLOBAL_FLOW_NO);

        if (rtn) {
            if (!identityService.isAliasExisted(SysProperty.GLOBAL_FLOW_NO, "")) {
                String message = "请设置别名为" + SysProperty.GLOBAL_FLOW_NO + "的流水号";
                throw new RuntimeException(message);
            }
            String flowNo = identityService.nextId(SysProperty.GLOBAL_FLOW_NO);
            processRun.setGlobalFlowNo(flowNo);
        }

        processRunService.add(processRun);
        return processRun;
    }

    /**
     * 获取子流程表单,要求子、父流程的表单对应的主表相同
     * @param formKey 表单key
     * @param parentFormDefId 父表单定义di
     * @param name 模型名称
     * @return
     */
    private String getExtSubProcessFormKey(String formKey, String parentFormDefId, String name) {
        FormDefService formDefService = AppUtil.getBean(FormDefService.class);
        FormDef formDef = formDefService.getDefaultPublishedByFormKey(formKey);
        String formDefId = formDef.getFormDefId();
        if (formDefId.equals(parentFormDefId)) {
            return formDefId;
        }else {
            FormDef parentFormDef = formDefService.getOneById(parentFormDefId);
            //表相同
            if(formDef.getTableId().equals(parentFormDef.getTableId())){
                return formDefId;
            }else{
                String msg = "外部子流程【"+name+"】设置的表单的主表与父流程的必须相同!";
                MessageUtil.addMsg(msg);
                throw new RuntimeException(msg);
            }
        }
    }
}
